/** @file    ServerTcpLS.cpp
 *  @author  Alessandro Polo
 *  @version $Id: ServerTcpLS.cpp 2821 2010-08-01 11:44:26Z alex $
 *  @brief
 * File containing methods for the wosh::network::libsockets::ServerTcpLS class.
 * The header for this class can be found in ServerTcpLS.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <framework/network/libsockets/ServerTcpLS.h>

 #include <framework/network/NetworkUtilities.h>
 #include <core/Utilities.h>
 #include <core/ObjectAllocator.h>

 #include <Sockets/ISocketHandler.h>
 #include <Sockets/Socket.h>
 #include <Sockets/TcpSocket.h>
 #include <Sockets/Utility.h>
 #include <Sockets/SocketHandler.h>
 #include <Sockets/Ipv4Address.h>
 #include <Sockets/Ipv6Address.h>

class ServerTcpWrapper : public Sockets::TcpSocket {
	public:
		ServerTcpWrapper( Sockets::SocketHandler& m ) : Sockets::TcpSocket(m) {
			this->parent = NULL;
			this->m_depth = 0;
		 }
		virtual ~ServerTcpWrapper() { }

	public:
		int Bind(Sockets::SocketAddress& ad,const std::string& protocol,int depth) {
			SOCKET s;
			if ( (s = CreateSocket(ad.GetFamily(), SOCK_STREAM, protocol)) == INVALID_SOCKET)
			{
				return -1;
			}
			// socket must be nonblocking for async connect
			if (!SetNonblocking(true, s))
			{
				closesocket(s);
				return -1;
			}
#ifndef _WIN32
int Errno = 0;
#endif
			if (bind(s, ad, ad) == -1)
			{
				Handler().LogError(this, "bind", Errno, StrError(Errno), Sockets::LOG_LEVEL_FATAL);
				closesocket(s);
				return -1;
			}
			if (listen(s, depth) == -1)
			{
				Handler().LogError(this, "listen", Errno, StrError(Errno), Sockets::LOG_LEVEL_FATAL);
				closesocket(s);
				return -1;
			}
			m_depth = depth;
			Attach(s);
			return 0;
		}

		void OnAccept()
		{
			Send("Local hostname : " + Sockets::Utility::GetLocalHostname() + "\n");
			Send("Local address : " + Sockets::Utility::GetLocalAddress() + "\n");
			Send("Number of sockets in list : " + Sockets::Utility::l2string(Handler().GetCount()) + "\n");
			Send("\n");
		}

		void OnConnect() {
			if ( this->parent != NULL )
				this->parent->OnConnect();
		}
		void OnDisconnect() {
			if ( this->parent != NULL )
				this->parent->OnDisconnect();
		}

	public:
		wosh::network::libsockets::ServerTcpLS* parent;
		int m_depth;

};


using namespace std;
namespace wosh {
 namespace network {
  namespace libsockets {

  WOSH_REGISTER(wosh::network::libsockets::ServerTcpLS, "wosh::network::ServerTcp", 1.0, _static_ServerTcpLS )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

ServerTcpLS::ServerTcpLS( ) : wosh::ThreadImpl(NULL, "ServerTcpLS.Thread"), wosh::network::ServerTcp()
 {
	this->sHandler = new Sockets::SocketHandler();
	this->serverWapper = new ServerTcpWrapper(*this->sHandler);
	this->serverWapper->parent = this;
 }

ServerTcpLS::~ServerTcpLS()
 {
	if ( this->isRunning() )
		unbindSocket();

	delete this->serverWapper; this->serverWapper = NULL;

	delete this->sHandler; this->sHandler = NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void ServerTcpLS::OnConnect() {
 }

void ServerTcpLS::OnDisconnect() {
 }
/*
void ServerTcp::incomingConnection( int socketId )
{
#ifdef _ServerTcp_DEBUG_ENABLED
	Log(LOG_INFO, ":incomingConnection() Socket#%d", socketId );
#endif
	if ( this->listener != NULL )
		this->listener->serverTcp_incomingConnection(socketId, this);
}
*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT ServerTcpLS::bindSocket( unsigned long timeout )
 {
	if ( isListening() ) {
#ifdef _ServerTcp_DEBUG_ENABLED
		Log(LOG_CRITICAL, ":bindSocket() Already Binded!" );
#endif
		return WRET_ERR_WRONG_STATE;
	 }
#ifdef _ServerTcp_DEBUG_ENABLED
	Log(LOG_VERBOSE, ":bindSocket(%u) ", timeout );
#endif
	WRESULT ret = startThread(timeout);
#ifdef _ServerTcp_DEBUG_ENABLED
	if ( WFAILED(ret) )
		Log(LOG_CRITICAL, ":bindSocket(%s,%u) Binding FAILED#%d [TimedOut]!", this->address.c_str(), timeout, ret );
	else {
		if ( timeout == 0 )
			Log(LOG_VERBOSE, ":bindSocket(%s) Asynch Binding..", this->address.c_str() );
		else
			Log(LOG_INFO, ":bindSocket(%s) Binding succedded.", this->address.c_str() );
	 }
#endif
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT ServerTcpLS::unbindSocket( unsigned long timeout )
 {
	if ( !isThreadRunning() ) {
#ifdef _ServerTcp_DEBUG_ENABLED
		Log(LOG_WARNING, ":unbindSocket() Not Binded!" );
#endif
		return WRET_ERR_WRONG_STATE;
	 }
#ifdef _ServerTcp_DEBUG_ENABLED
	Log(LOG_VERBOSE, ":unbindSocket(%u) ", timeout );
#endif
	WRESULT ret = ThreadImpl::quitThread(timeout);
	if ( WFAILED(ret) )
		Log(LOG_CRITICAL, ":unbindSocket(%u) FAILED#%d [TimedOut]!", timeout, ret );
	else {
		if ( timeout == 0 )
			Log(LOG_VERBOSE, ":unbindSocket() Asynch UnBinding.." );
		else
			Log(LOG_INFO, ":unbindSocket() UnBinding succedded." );
	 }
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// THREAD RUN

bool ServerTcpLS::initializingThread()
 {
	port_t p = (unsigned int)this->port;
	int binded = -1;
	int depth = 20;
#ifdef ENABLE_IPV6
# ifdef IPPROTO_IPV6
		if ( IsIpv6() ) {
			Sockets::Ipv6Address ad(intf, p);
			if ( ad.IsValid() ) {
				binded = this->serverWapper->Bind(ad, "tcp", depth);
			}
			//this->sHandler->LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
		}
		else
# endif
#endif
		{
			if ( this->address == "0.0.0.0" ) {
				Sockets::Ipv4Address ad(p);
				if ( ad.IsValid() )
					binded = this->serverWapper->Bind(ad, "tcp", depth);
			 }
			else {
				Sockets::Ipv4Address ad(this->address, p);
				if ( ad.IsValid() )
					binded = this->serverWapper->Bind(ad, "tcp", depth);
			 }
			//this->sHandler->LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
		}

	if ( binded != 0 ) {
#ifdef _ServerTcp_DEBUG_ENABLED
		Log(LOG_CRITICAL, ":runThread() FAILED : Connecting Socket to '%s' at port %d.. []", this->address.c_str(), p );
#endif
		return false;
	 }

	this->sHandler->Add(this->serverWapper);
	this->sHandler->Select(1,0);
 	return true;
 }

void ServerTcpLS::runThread()
 {
	while( this->running && this->sHandler->GetCount() ) {
		this->sHandler->Select(1,0);
	 }
 }

void ServerTcpLS::exitingThread()
 { 
#ifdef _ServerTcp_DEBUG_ENABLED
	Log(LOG_VERBOSE, ":exitingThread() Closing Socket.." );
#endif
	int closed = this->serverWapper->Close();
#ifdef _ServerTcp_DEBUG_ENABLED
	if ( closed ) {
		Log(LOG_CRITICAL, ":exitingThread() FAILED#%d Closing Socket..", closed );
	 }
#endif
 }
 
 /*
#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6
		if (IsIpv6()) {
			Ipv6Address ad(port); return Bind(ad, depth);
		 } else
#endif
#endif
		{ Ipv4Address ad(port); return Bind(ad, depth); }
 }
*/
////////////////////////////////////////////////////////////////////////////////////////////////// THREAD RUN
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool ServerTcpLS::isListening() const {
	return isThreadRunning() && this->serverWapper->IsConnected();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

  }; // namespace libsockets
 }; // namespace network
}; // namespace wosh
